package org.lestr.secuel.sogl.maths;

import org.lestr.secuel.m3ds.maths.IVecteur3d;
import org.lestr.secuel.sogl.Usine;

public class BoiteOrientee {


    /** Retourne vrai si deux boites sont en collision */
    public static boolean estEnCollision(BoiteOrientee boite1,
                                         BoiteOrientee boite2) {

        // Variables locales
        IVecteur3d centreBoite1 = boite1.getCentre();
        IVecteur3d centreBoite2 = boite2.getCentre();

        //-------------------------------------------//
        // On test d'abord à l'aide de l'algo simple //
        //-------------------------------------------//

        // On détermine le distance entre les centres des deux boites
        double distanceCentres = (centreBoite2.obtenirX() - centreBoite1.obtenirX()) * (centreBoite2.obtenirX() - centreBoite1.obtenirX())
                                 + (centreBoite2.obtenirY() - centreBoite1.obtenirY()) * (centreBoite2.obtenirY() - centreBoite1.obtenirY())
                                 + (centreBoite2.obtenirZ() - centreBoite1.obtenirZ()) * (centreBoite2.obtenirZ() - centreBoite1.obtenirZ());
        distanceCentres = Math.sqrt(distanceCentres);

        // On determine le rayon des boites
        double rayonBoite1 = boite1.getLongueur(0) > boite1.getLongueur(1) ? boite1.getLongueur(0) : boite1.getLongueur(1);
        rayonBoite1 = boite1.getLongueur(2) > rayonBoite1 ? boite1.getLongueur(2) : rayonBoite1;
        double rayonBoite2 = boite2.getLongueur(0) > boite2.getLongueur(1) ? boite2.getLongueur(0) : boite2.getLongueur(1);
        rayonBoite2 = boite2.getLongueur(2) > rayonBoite2 ? boite2.getLongueur(2) : rayonBoite2;

        // Si la somme des rayon est plus petite que la distance entre les centre, pas collision.
        if (rayonBoite1 + rayonBoite2 < distanceCentres)
            return false;

        //--------------------------------------------------------------//
        // Si collision, on est plus précis et on utilise l'algo évolué //
        //--------------------------------------------------------------//

        // Projete les points des deux boites sur les axes de la boite 1
        for (int i = 0; i <= 2; i = i + 1) {

            // Les distances des projetés de P1 sont simples
            double distanceBoite1Min = boite1.getLongueur(i) / 2;

            // Initialise les projections min et max de la boite 2 (c'est moins simple)
            double distanceBoite2Min = Float.POSITIVE_INFINITY;

            // Parcours les points de P2
            for (int j = 0; j <= 7; j = j + 1) {

                // Projete le point Courant de P1 sur l'axe de projection courant
                double distance = projeterPoint(boite2.getPoint(j), boite1.getAxe(i), centreBoite1);

                // Si c'est la plus petite positivement ou la plus grande négativement, mémorise
                if (distance < distanceBoite2Min)
                    distanceBoite2Min = distance;

            }

            // S'il n'y a pas de croisement, il n'y a pas de collision
            if (distanceBoite1Min < distanceBoite2Min)
                return false;

        }

        // Projete les points des deux boites sur les axes de la boite 2
        for (int i = 0; i <= 2; i = i + 1) {

            // Les distances des projetés de P1 sont simples
            double distanceBoite2Min = boite2.getLongueur(i) / 2;

            // Initialise les projections min et max de la boite 2 (c'est moins simple)
            double distanceBoite1Min = Float.POSITIVE_INFINITY;

            // Parcours les points de P2
            for (int j = 0; j <= 7; j = j + 1) {

                // Projete le point Courant de P1 sur l'axe de projection courant
                double distance = projeterPoint(boite1.getPoint(j), boite2.getAxe(i), centreBoite2);

                // Si c'est la plus petite positivement ou la plus grande négativement, mémorise
                if (distance < distanceBoite1Min)
                    distanceBoite1Min = distance;

            }

            // S'il n'y a pas de croisement, il n'y a pas de collision
            if (distanceBoite2Min < distanceBoite1Min)
                return false;

        }

        // Il y a collision
        return true;

    }// FIN estEnCollision


    /** Projete un point sur un axe munit d'un centre, et retourne la distance (orientée) du projeté au centre */
    private static double projeterPoint(IVecteur3d point,
                                        IVecteur3d vecteurAxe,
                                        IVecteur3d centre) {

        IVecteur3d temp = Usine.getSingleton().vecteur3D(point.obtenirX(), point.obtenirY(), point.obtenirZ());
        temp.soustraire(centre);
        double produit = vecteurAxe.produitVectoriel(temp);
        return produit >= 0 ? produit : -produit;

    }// FIN projeterPoint


    // Déclaration des attributs
    private IVecteur3d origine;


    private IVecteur3d[] axes;


    private double[] longueurs;


    /** Constructeur */
    public BoiteOrientee() {

        // Instanciation des attributs
        origine = Usine.getSingleton().vecteur3D();
        axes = new IVecteur3d[3];
        longueurs = new double[3];

    }// FIN Constructeur


    /** Retourne un axe */
    public IVecteur3d getAxe(int index) {

        return axes[index];

    }// FIN retAxe


    /** Défini un axe */
    public void setAxe(int index,
                       IVecteur3d axe) {

        axes[index] = axe;

    }// FIN defAxe


    /** Retourne une longueur */
    public double getLongueur(int index) {

        return longueurs[index];

    }// FIN retLongueur


    /** Défini une longueur */
    public void setLongueur(int index,
                            double longueur) {

        longueurs[index] = longueur;

    }// FIN defLongueur


    /** Retourne un point de la boite (il y a 8 points) */
    public IVecteur3d getPoint(int index) {

        // Variables locales
        IVecteur3d rslt = Usine.getSingleton().vecteur3D();
        IVecteur3d tmp1 = Usine.getSingleton().vecteur3D();
        IVecteur3d tmp2 = Usine.getSingleton().vecteur3D();

        // On déplace suivant les axes
        switch (index) {

            case 1:
                break;
            case 2:
                rslt.additionner(axes[0]);
                rslt.redimensionner(longueurs[0]);
                break;
            case 3:
                rslt.additionner(axes[1]);
                rslt.redimensionner(longueurs[1]);
                break;
            case 4:
                rslt.additionner(axes[2]);
                rslt.redimensionner(longueurs[2]);
                break;

            case 5:
                rslt.additionner(axes[0]);
                rslt.redimensionner(longueurs[0]);
                tmp1.additionner(axes[1]);
                tmp1.redimensionner(longueurs[1]);
                rslt.additionner(tmp1);
                break;

            case 6:
                rslt.additionner(axes[0]);
                rslt.redimensionner(longueurs[0]);
                tmp1.additionner(axes[2]);
                tmp1.redimensionner(longueurs[2]);
                rslt.additionner(tmp1);
                break;

            case 7:
                rslt.additionner(axes[1]);
                rslt.redimensionner(longueurs[1]);
                tmp1.additionner(axes[2]);
                tmp1.redimensionner(longueurs[2]);
                rslt.additionner(tmp1);
                break;

            case 8:
                rslt.additionner(axes[1]);
                rslt.redimensionner(longueurs[1]);
                tmp1.additionner(axes[2]);
                tmp1.redimensionner(longueurs[2]);
                tmp2.additionner(axes[3]);
                tmp2.redimensionner(longueurs[3]);
                rslt.additionner(tmp1);
                rslt.additionner(tmp2);
                break;

        }

        // Au résultat on ajoute l'origine
        rslt.additionner(origine);

        // Retour du résultat
        return rslt;

    }// FIN retPoint


    /** Retourne le centre de la boite */
    public IVecteur3d getCentre() {

        // Variables locales
        IVecteur3d rslt = Usine.getSingleton().vecteur3D();

        // Fait la somme des points
        for (int i = 0; i <= 7; i = i + 1)
            rslt.additionner(getPoint(i));

        // Fait la moyenne des points
        rslt.définirX(rslt.obtenirX() / 8);
        rslt.définirY(rslt.obtenirY() / 8);
        rslt.définirZ(rslt.obtenirZ() / 8);

        // Retour du résultat
        return rslt;

    }// FIN retCentre


    /** Retourne l'origine de la boite */
    public IVecteur3d getOrigine() {

        return origine;

    }// FIN retOrigine


    /** Défini l'origine de la boite */
    public void setOrigine(IVecteur3d origine) {

        this.origine = origine;

    }// FIN defOrigine


}// FIN Classe
